/*
 * Copyright 2002-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.aop.framework;

import org.springframework.core.NamedThreadLocal;
import org.springframework.lang.Nullable;

/**
 *
 * 包含用于获取有关当前 AOP 调用信息的静态方法的类。
 * 如果 AOP 框架配置为公开当前代理（不是默认的），则 {@code currentProxy()} 方法可用。
 * 它返回正在使用的 AOP 代理。目标对象或通知可以使用它来进行通知调用，就像可以在 EJB 中使用 {@code getEJBObject()} 一样。
 * 他们还可以使用它来查找建议配置。
 * Spring 的 AOP 框架默认不公开代理，因为这样做会产生性能成本。
 * 此类中的功能可能会被需要访问调用资源的目标对象使用。
 * 但是，如果有合理的替代方案，则不应使用这种方法，因为它使应用程序代码依赖于 AOP 下的使用，尤其是 Spring AOP 框架。
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @since 13.03.2003
 */
public final class AopContext {

	/**
	 * 与此线程相关的AOP代理的ThreadLocal持有人。
	 * 将包含{@code null}，除非 "exposeProxy "属性在控制代理配置已被设置为 "true"。
	 * @see ProxyConfig#setExposeProxy
	 */
	private static final ThreadLocal<Object> currentProxy = new NamedThreadLocal<>("Current AOP proxy");


	private AopContext() {
	}


	/**
	 * 尝试返回当前的AOP代理。这个方法只有在以下情况下才能使用
	 * 调用方法是通过AOP调用的，并且AOP框架已经被设置为以暴露代理。
	 * 否则，该方法将抛出一个IllegalStateException。
	 * @return 当前的AOP代理（从不返回{@code null}）。
	 * @throws IllegalStateException 如果找不到代理，因为该方法是在AOP调用上下文之外调用的，或者是因为AOP框架没有被配置为公开代理。
	 */
	public static Object currentProxy() throws IllegalStateException {
		Object proxy = currentProxy.get();
		if (proxy == null) {
			throw new IllegalStateException(
					"Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available, and " +
							"ensure that AopContext.currentProxy() is invoked in the same thread as the AOP invocation context.");
		}
		return proxy;
	}

	/**
	 *  通过{@code currentProxy()}方法使给定的代理可用。
	 *  注意，调用者应注意适当地保留旧值。
	 * @param proxy the proxy to expose (or {@code null} to reset it)
	 * @return the old proxy, which may be {@code null} if none was bound
	 * @see #currentProxy()
	 */
	@Nullable
	static Object setCurrentProxy(@Nullable Object proxy) {
		Object old = currentProxy.get();
		if (proxy != null) {
			currentProxy.set(proxy);
		}
		else {
			currentProxy.remove();
		}
		return old;
	}

}
